/** @file         http_server.c
 *  @brief        简要说明
 *  @details      详细说明
 *  @author       lzm
 *  @date         2021-06-12 17:19:20
 *  @version      v1.0
 *  @copyright    Copyright By lizhuming, All Rights Reserved
 *
 **********************************************************
 *  @LOG 修改日志:
 **********************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>

#include "atomic_lzm.h"

#define CLIENT_BUFF_MAX 1024 // 客户端接收缓冲区 size
#define MY_SERVER_PORT 8080 // 服务端监听端口
#define CLIENT_PENDING_NUM 10 // 内核未完成队列的客户端缓冲数
#define CLIENT_MAX_NUM 20 // 客户端连接数限制

atomic_lzm_t g_client_num;

// http 状态行+响应头部+空行
static const char http_html_header[] = "HTTP/1.1 200 OK\r\nConten-type:text/html\r\n\r\n";

// http 响应内容 html代码
static const char http_index_html[] = "<html><head><title>Congrats!</title></head>\
 <body><h1 align=\"center\">Hello World!</h1>\
 <h2 align=\"center\">Welcome to Fire my HTTP Server!</h1>\
 <p align=\"center\">This is a small test page, served by httpserver-netconn.</p>\
 <p align=\"center\"><a href=\"https://www.cnblogs.com/lizhuming//\">\
 <font size=\"12\"> lizhuming-bk </font> </a></p>\
 <a href=\"https://www.cnblogs.com/lizhuming//\">\
 <p align=\"center\"><img src=\"https://sc.chinaz.com/tupian/170914577172.htm\" /></a>\
 </body></html>";




/**
 * @name   client_fun
 * @brief  线程函数，处理 client
 * @param  arg：资源
 * @retval 
 * @author lzm
 */
void *client_fun(void *arg)
{
	int recv_len = 0; // 接收长度
	char recv_buff[CLIENT_BUFF_MAX] = "";
	long connfd = (long)arg; // 已连接socket。 值传递（地址传递时注意多线程并发问题）

	// [1] 接收数据
	if((recv_len = recv(connfd, recv_buff, sizeof(recv_buff), 0)) > 0)
	{
		printf("[%ld]recv_buff:%s\r\n", connfd, recv_buff); // 打印数据

        /* 判断 HTTP 头 */
        /* GET 命令 */
        if(recv_len >= 5 &&
           recv_buff[0]=='G' &&
           recv_buff[1]=='E' &&
           recv_buff[2]=='T' &&
           recv_buff[3]==' ' &&
           recv_buff[4]=='/')
        {
            /* 发送数据头 */
            send(connfd, http_html_header, sizeof(http_html_header)-1, 0);
            /* 发送内容 */
            send(connfd, http_index_html, sizeof(http_index_html)-1, 0);
        }
	}

	// [2] 关闭 socket
	printf("client closed!\r\n");
	close(connfd); // 关闭 socket
    //sem_post(&sem_client);
    atomic_lzm_sub(&g_client_num);
    printf("can link num is [%d]\n", CLIENT_MAX_NUM-atomic_lzm_get(&g_client_num));
	return NULL;
}



/**
 * @name   main
 * @brief  main函数，服务端处理
 * @param  
 * @retval 
 * @author lzm
 */
int main(int argc, char *argv[])
{
	int sockfd = 0; // 监听 socket
	long connfd = 0; // 已连接 socket 。long 避免使用 64bit 机器而出错
	int ret = 0; // 返回缓冲
	struct sockaddr_in local_server_addr; // 本地服务器地址
	unsigned short local_server_port = MY_SERVER_PORT; // 本地服务器监听端口
	pthread_t thread_client_id; // client 线程 id

    atomic_lzm_init(&g_client_num, 0);

	printf("TCP server started at port [%d]!\n", local_server_port);

	// [1] 创建套接字
	printf("create server socket!\n");
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		printf("%s-%s-%d:sockfd create faild!", __FILE__, __FUNCTION__, __LINE__);
		perror("socket create error");
		exit(-1);
	}

	// [2] 初始化地址数据
	printf("init server address!\n");
	bzero(&local_server_addr, sizeof(local_server_addr)); // 初始化服务器地址
	local_server_addr.sin_family = AF_INET;
	local_server_addr.sin_port = htons(local_server_port);
	local_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	// [3] 绑定
	printf("bing server socket and addr!\n");
	ret = bind(sockfd, (struct sockaddr*)&local_server_addr, sizeof(local_server_addr));
	if(ret != 0)
	{
		printf("%s-%s-%d:sockfd bind faild!", __FILE__, __FUNCTION__, __LINE__);
		perror("socket bind error");
		close(sockfd);
		exit(-1);
	}

	// [4] 监听
	printf("listen server socket!\n");
	ret = listen(sockfd, CLIENT_PENDING_NUM);
	if(ret != 0)
	{
		printf("%s-%s-%d:sockfd listen faild!", __FILE__, __FUNCTION__, __LINE__);
		perror("socket listen error");
		close(sockfd);
		exit(-1);
	}

	//sem_init(&sem_client, 0, CLIENT_MAX_NUM);
    
	printf("accept!\n");

	// [5] 处理 client
	while(1)
	{
		char client_ip[INET_ADDRSTRLEN] = ""; // use for save client ip
		struct sockaddr_in client_addr; // use for save client address
		socklen_t client_len = sizeof(client_addr); // 必须初始化

        //sem_wait(&sem_client);

		// [5][1] 获取一个已建立的连接
		connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
		if(connfd < 0)
		{
			printf("%s-%s-%d:sockfd accept faild!", __FILE__, __FUNCTION__, __LINE__);
			perror("accept error");
			continue;
		}

        if(atomic_lzm_get(&g_client_num) >= CLIENT_MAX_NUM)
        {
            close(connfd);
        }
        else
        {
            // [5][2] 处理客户端数据
            inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
            printf("clien ip = [%s], port = [%d]\n", client_ip, ntohs(client_addr.sin_port));

            if(connfd > 0)
            {
                // 线程处理客户端数据
                pthread_create(&thread_client_id, NULL, (void *)client_fun, (void *)connfd); // creat thread。注意 64bit 机器地址是8byte的
                pthread_detach(thread_client_id); // thread 分离。即时，线程回调函数结束时自动回收该线程资源
                atomic_lzm_add(&g_client_num);
            }
        }
        printf("can link num is [%d]\n", CLIENT_MAX_NUM-atomic_lzm_get(&g_client_num));
	}
	
	// [6] 关闭 server socket
	close(sockfd);

	return 0;
}
